<?php
namespace phiction;

class config
{
    public $debugging = false;
    public $posix_compliant = false;
    public $colorful = false;
    public $pretty = false;
    public $fragment = false;
    public $request = '';
    public $invert_negative = false;
    public $args = null;
    public $is_post = false;

    private $options;

    function detect(): self
    {
        $this->parse_args();

        if ($this->bool_opt('h') || $this->bool_opt('help')) {
            echo "OPTIONS:\n";
            echo "\n";
            echo "  -h        show help\n";
            echo "  -d        force enable debug (disabled by default)\n";
            echo "  -c        force enable color (detected by platform)\n";
            echo "  -p        force enable pretty (default to true on debug, false on no-debug)\n";
            echo "  -w        lie to be MS Windows (default to false)\n";
            echo "  -r URI    request URI\n";
            echo "  -P        use POST method (default to false, i.e. use GET method)\n";
            echo "  -a ARGS   arguments to the request\n";
            echo "  -i        invert negative tests into positive tests to see the error (default to false)\n";
            echo "\n";
            echo "  --help        show help\n";
            echo "  --no-debug    force disable debug\n";
            echo "  --no-color    force disable color\n";
            echo "  --no-pretty   force disable pretty\n";
            echo "  --no-posix    force disable posix (detected by platform)\n";
            echo "\n";
            if ($this->is_mswin()) {
                echo "NOTE: It is always no-posix on Windows.\n";
                echo "\n";
            }
            exit(0);
        }

        # debugging
        $this->debugging = $this->bool_opt('d') && !$this->bool_opt('no-debug');

        # posix compliant
        // XXX: This only work with the assumption that,
        //      this script will only be run in the following platform:
        //      - Linux
        //      - Mac OS X
        //      - Windows (native)
        //      - Windows (cygwin or mingw or msys)
        if (!$this->is_mswin())
            $this->posix_compliant = !$this->bool_opt('no-posix');

        # colorful
        $this->colorful = ($this->posix_compliant || $this->bool_opt('c')) && !$this->bool_opt('no-color');

        # pretty
        $this->pretty = ($this->debugging || $this->bool_opt('p')) && !$this->bool_opt('no-pretty');

        # request and fragment
        $uri = parse_url($this->str_opt('r'));
        $this->request = self::get_or($uri, 'path', '');
        $this->fragment = self::get_or($uri, 'query', '') === 'fragment';

        # is post
        $this->is_post = $this->bool_opt('P');

        # args
        $args = self::get_or($this->options, 'a', []);
        if (is_array($args)) $this->args = $args;
        else parse_str($args, $this->args);
        $this->args['HTTPS'] = array_key_exists('HTTPS', $_SERVER);

        # invert negative
        $this->invert_negative = $this->bool_opt('i');

        return $this;
    }

    static function detected(): self
    {
        return (new self)->detect();
    }

    private function is_mswin(): bool
    {
        $fake = $this->bool_opt('w');
        return strtolower(substr(PHP_OS, 0, 3)) === 'win' || $fake;
    }

    private function parse_args()
    {
        # cli
        if (PHP_SAPI === 'cli') {
            $this->options = getopt('hdcpwr:Pa:i', ['help', 'no-debug', 'no-color', 'no-pretty', 'no-posix']);
            $this->options = self::sanitize_options($this->options);
        }

        # server
        else {
            $this->options = [];

            $this->options['h'] = (bool)getenv('PHICTION_HELP');
            $this->options['d'] = (bool)getenv('PHICTION_DEBUG');
            $this->options['c'] = (bool)getenv('PHICTION_COLOR');
            $this->options['p'] = (bool)getenv('PHICTION_PRETTY');
            $this->options['w'] = (bool)getenv('PHICTION_WINDOWS');
            $this->options['i'] = (bool)getenv('PHICTION_INVERT_NEGATIVE');

            $this->options['help'] = (bool)getenv('PHICTION_HELP');
            $this->options['no-debug'] = (bool)getenv('PHICTION_NO_DEBUG');
            $this->options['no-color'] = (bool)getenv('PHICTION_NO_COLOR');
            $this->options['no-pretty'] = (bool)getenv('PHICTION_NO_PRETTY');
            $this->options['no-posix'] = (bool)getenv('PHICTION_NO_POSIX');

            $this->options['r'] = self::get_or($_SERVER, 'REQUEST_URI', '');

            switch ($_SERVER['REQUEST_METHOD']) {
                case 'GET':
                    $args = $_COOKIE;
                    if (array_key_exists('throw', $_GET)) $args['throw'] = $_GET['throw'];
                    $this->options['a'] = $args;
                    $this->options['P'] = false;
                    break;
                case 'POST':
                    $this->options['a'] = array_merge($_COOKIE, $_POST);
                    $this->options['P'] = true;
                    break;
                default:
                    throw new \InvalidArgumentException("only support GET or POST method");
            }
        }
    }

    private function bool_opt(string $opt): bool
    {
        return self::get_or($this->options, $opt, false);
    }

    private function str_opt(string $opt): string
    {
        return self::get_or($this->options, $opt, '');
    }

    private static function get_or(array $arr, string $key, $default)
    {
        return array_key_exists($key, $arr) ? $arr[$key] : $default;
    }

    private static function sanitize_options(array $opts): array
    {
        foreach ($opts as &$value) {
            if ($value === false)
                $value = true;
        }
        return $opts;
    }
}

